﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using OSGeo.GDAL;

using DotSpatial.Data;

using System.Diagnostics;

using System.IO;






namespace FXnaGeoCenter
{
    public  class GdalService
    {

        protected GdalService()
        {

            OSGeo.GDAL.Gdal.AllRegister();
 
        }


        static GdalService currentService;

        public static GdalService CurrentService
        {
            get
            {


                if (currentService == null)
                {
                    currentService = new GdalService();

                }

                return GdalService.currentService;

            }
        }





        public void ReadFile(string filename)
        {

            OSGeo.GDAL.Driver Driver;

            OSGeo.GDAL.Dataset Dataset;

            OSGeo.GDAL.DataType DataType;

            OSGeo.GDAL.Band Band;

            if (System.IO.File.Exists(filename) == false)
            {
                ////return null;
            }

            Dataset = OSGeo.GDAL.Gdal.Open(filename, Access.GA_ReadOnly);

            Driver = Dataset.GetDriver();

            Band = Dataset.GetRasterBand(1);

            DataType = Band.DataType;



            


 
        }



        /// <summary>
        /// 为某些显示用途，提供低精度版本，而不去管之前实际的值，
        /// 提供采样点数
        /// nx,ny，表示实际的点数
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public double[,] ReadDataForDisplay(string filename,int nx,int ny)
        {

            //需要考虑很多东西，比如dx,dy，投影转换，之后返回值肯定会有比较大的变化

            double[,] data = new double[nx, ny];




            return null;
        }





        /// <summary>
        /// Creates a new image given the specified file format
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <param name="inRam">if set to <c>true</c> should load entire file in ram.</param>
        /// <param name="progHandler">The prog handler.</param>
        /// <param name="bandType">Type of the band.</param>
        /// <returns></returns>
        public IImageData Create(string fileName, int width, int height, bool inRam, IProgressHandler progHandler, ImageBandType bandType)
        {
            Gdal.AllRegister();
            Driver d = GetDriverByExtension(fileName);
            if (d == null) return null;
            Dataset ds;
            if (bandType == ImageBandType.ARGB)
            {
                ds = d.Create(fileName, width, height, 4, DataType.GDT_Byte, new string[] { });
            }
            else if (bandType == ImageBandType.RGB)
            {
                ds = d.Create(fileName, width, height, 3, DataType.GDT_Byte, new string[] { });
            }
            else if (bandType == ImageBandType.PalletCoded)
            {
                ds = d.Create(fileName, width, height, 1, DataType.GDT_Byte, new string[] { });
            }
            else
            {
                ds = d.Create(fileName, width, height, 1, DataType.GDT_Byte, new string[] { });
            }

            return new GdalImage(fileName, ds, bandType);
        }




        /// <summary>
        /// http://www.gdal.org/formats_list.html
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static Driver GetDriverByExtension(string filename)
        {
            string ext = Path.GetExtension(filename);
            if (ext != null)
            {
                ext = ext.Replace(".", String.Empty).ToLower();
                switch (ext)
                {
                    case @"asc": return Gdal.GetDriverByName("AAIGrid");
                    case @"gen":
                    case @"thf": return Gdal.GetDriverByName("ADRG");
                    case @"adf": return Gdal.GetDriverByName("AIG");
                    case @"blx":
                    case @"bxl": return Gdal.GetDriverByName("BLX");
                    case @"bag":
                        return Gdal.GetDriverByName("BAG");
                    case @"bmp":
                        return Gdal.GetDriverByName("BMP");
                    case @"kap":
                        return Gdal.GetDriverByName("BSB");
                    case @"bt":
                        return Gdal.GetDriverByName("BT");
                    case @"dim":
                        return Gdal.GetDriverByName("DIM");
                    case @"doq":
                        return Gdal.GetDriverByName("DOQ2");
                    case @"dt0":
                    case @"dt1":
                    case @"dt2":
                        return Gdal.GetDriverByName("DTED");
                    case @"ecw":
                        return Gdal.GetDriverByName("ECW");
                    case @"htr":
                        return Gdal.GetDriverByName("EHdr");
                    case @"ers":
                        return Gdal.GetDriverByName("ERS");
                    case @"nl":
                        return Gdal.GetDriverByName("ESAT");
                    case @"gif":
                        return Gdal.GetDriverByName("GIF");
                    case @"tif":
                        return Gdal.GetDriverByName("GTiff");
                    case @"jpg":
                        return Gdal.GetDriverByName("JPEG");
                    case @"jp2":
                    case @"j2k":
                        return Gdal.GetDriverByName("JPEG2000");
                    case @"ppm":
                    case @"pgm":
                        return Gdal.GetDriverByName("PNM");
                    case @"png":
                        return Gdal.GetDriverByName("PNG");
                    case @"rik":
                        return Gdal.GetDriverByName("RIK");
                    case @"rsw":
                    case @"mtw":
                        return Gdal.GetDriverByName("RMF");
                    case @"ter":
                        return Gdal.GetDriverByName("TERRAGEN");
                    case @"dem":
                        return Gdal.GetDriverByName("USGSDEM");
                    case @".vrt":
                        return Gdal.GetDriverByName("VRT");
                    case @"xpm":
                        return Gdal.GetDriverByName("XPM");
                }
            }
            else
            {
                return Gdal.GetDriverByName("AAIGrid");
            }
            return null;
        }






        /// <summary>
        ///
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private IImageData OpenFile(string fileName)
        {
            Dataset dataset;
            try
            {
                dataset = Gdal.Open(fileName, Access.GA_Update);
            }
            catch
            {
                try
                {
                    dataset = Gdal.Open(fileName, Access.GA_ReadOnly);
                }
                catch (Exception ex)
                {
                    throw new GdalException(ex.ToString());
                }
            }

            Band red = dataset.GetRasterBand(1);

         

            ColorInterp bandType = red.GetRasterColorInterpretation();
            if (bandType != ColorInterp.GCI_PaletteIndex &&
                bandType != ColorInterp.GCI_GrayIndex &&
                bandType != ColorInterp.GCI_RedBand &&
                bandType != ColorInterp.GCI_AlphaBand)
            {
                // This is an image, not a raster, so return null.
                dataset.Dispose();
                dataset = null;
                return null;
            }

            GdalImage result = new GdalImage(fileName);
            if (result.Width > 8000 || result.Height > 8000)
            {
                // Firstly, if there are pyramids inside of the GDAL file itself, we can just work with this directly,
                // without creating our own pyramid image.

                // For now, we can't get fast, low-res versions without some kind of pyramiding happening.
                // since that can take a while for huge images, I'd rather do this once, and create a kind of
                // standardized file-based pyramid system.  Maybe in future pyramid tiffs could be used instead?
                string pyrFile = Path.ChangeExtension(fileName, ".mwi");
                if (File.Exists(pyrFile))
                {
                    if (File.Exists(Path.ChangeExtension(pyrFile, ".mwh")))
                    {
                        return new PyramidImage(fileName);
                    }
                    File.Delete(pyrFile);
                }

                GdalImageSource gs = new GdalImageSource(fileName);
                PyramidImage py = new PyramidImage(pyrFile, gs.Bounds);
                int width = gs.Bounds.NumColumns;
                int blockHeight = 64000000 / width;
                if (blockHeight > gs.Bounds.NumRows) blockHeight = gs.Bounds.NumRows;
                int numBlocks = (int)Math.Ceiling(gs.Bounds.NumRows / (double)blockHeight);



                //ProgressMeter pm = new ProgressMeter(ProgressHandler, "Copying Data To Pyramids", numBlocks * 2);
             
                //Application.DoEvents();



                for (int j = 0; j < numBlocks; j++)
                {
                    int h = blockHeight;
                    if (j == numBlocks - 1)
                    {
                        h = gs.Bounds.NumRows - j * blockHeight;
                    }
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    byte[] vals = gs.ReadWindow(j * blockHeight, 0, h, width, 0);
                    Debug.WriteLine("Reading Value time: " + sw.ElapsedMilliseconds);


                    //pm.CurrentValue = j * 2 + 1;

                    sw.Reset();
                    sw.Start();
                    py.WriteWindow(vals, j * blockHeight, 0, h, width, 0);
                    sw.Stop();
                    Debug.WriteLine("Writing Pyramid time: " + sw.ElapsedMilliseconds);


                    //pm.CurrentValue = (j + 1) * 2;
                }
                gs.Dispose();


                //pm.Reset();
                //py.ProgressHandler = ProgressHandler;



                py.CreatePyramids();
                py.WriteHeader(pyrFile);
                return py;
            }
            result.Open();
            return result;
        }





    }
}
